﻿using BBDown.Core.Protobuf;
using Google.Protobuf;
using System.Text;
using static BBDown.Core.Entity.Entity;
using static BBDown.Core.Util.HTTPUtil;
using System.Text.RegularExpressions;
using System.Text.Json;

namespace BBDown.Core.Util;

public static partial class SubUtil
{
    //https://i0.hdslb.com/bfs/subtitle/subtitle_lan.json
    public static (string, string) GetSubtitleCode(string key)
    {
        //zh-hans => zh-Hans
        if (NonCapsRegex().Match(key) is { Success: true } result)
        {
            var v = result.Value;
            key = key.Replace(v, v.ToUpper());
        }

        return key switch
        {
            "ai-Zh"             => ("chi", "中文（简体, AI识别）"),
            "ai-En"             => ("eng", "English(generated by ai)"),
            "zh-CN"             => ("chi", "中文（简体）"),
            "zh-HK"             => ("chi", "中文（香港繁體）"),
            "zh-Hans"           => ("chi", "中文（简体）"),
            "zh-TW"             => ("chi", "中文（台灣繁體）"),
            "zh-Hant"           => ("chi", "中文（繁體）"),
            "en-US"             => ("eng", "English(USA)"),
            "ja"                => ("jpn", "日本語"),
            "ko"                => ("kor", "한국어"),
            "zh-SG"             => ("chi", "中文（新加坡）"),
            "ab"                => ("abk", "Аҳәынҭқарра"),
            "aa"                => ("aar", "Qafár af"),
            "af"                => ("afr", "Afrikaans"),
            "sq"                => ("alb", "Gjuha shqipe"),
            "ase"               => ("ase", "American Sign Language"),
            "am"                => ("amh", "አማርኛ"),
            "arc"               => ("arc", "ܐܪܡܝܐ"),
            "hy"                => ("arm", "հայերեն"),
            "as"                => ("asm", "অসমীয়া"),
            "ay"                => ("aym", "Aymar aru"),
            "az"                => ("aze", "Azərbaycan"),
            "bn"                => ("ben", "বাংলা ভাষার"),
            "ba"                => ("bak", "Башҡорттеле"),
            "eu"                => ("baq", "Euskara"),
            "be"                => ("bel", "беларуская мова biełaruskaja mova"),
            "bh"                => ("bih", "Bihar"),
            "bi"                => ("bis", "Bislama"),
            "bs"                => ("bos", "босански"),
            "br"                => ("bre", "Breton"),
            "bg"                => ("bul", "български"),
            "yue"               => ("chi", "粵語"),
            "yue-HK"            => ("chi", "粵語（中國香港）"),
            "ca"                => ("cat", "català"),
            "chr"               => ("chr", "ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ"),
            "cho"               => ("cho", "Chahta'"),
            "co"                => ("cos", "lingua corsa"),
            "hr"                => ("hrv", "Hrvatska"),
            "cs"                => ("cze", "čeština"),
            "da"                => ("dan", "Dansk"),
            "nl"                => ("dut", "Nederlands"),
            "nl-BE"             => ("dut", "Nederlands(Belgisch)"),
            "nl-NL"             => ("dut", "Nederlands(Nederlands)"),
            "dz"                => ("dzo", "རྫོང་ཁ།"),
            "en"                => ("eng", "English"),
            "en-CA"             => ("eng", "English(Canada)"),
            "en-IE"             => ("eng", "English(Ireland)"),
            "en-GB"             => ("eng", "English(UK)"),
            "eo"                => ("epo", "Esperanto"),
            "et"                => ("est", "Eestlane"),
            "fo"                => ("fao", "føroyskt"),
            "fj"                => ("fij", "Vakaviti"),
            "fil"               => ("phi", "Pilipino"),
            "fi"                => ("fin", "Suomi"),
            "fr"                => ("fre", "Français"),
            "fr-BE"             => ("fre", "Français(Belgique)"),
            "fr-CA"             => ("fre", "Français(Canada)"),
            "fr-FR"             => ("fre", "Français(La France)"),
            "fr-CH"             => ("fre", "Français(Suisse)"),
            "ff"                => ("ful", "Fulani"),
            "gl"                => ("glg", "galego"),
            "ka"                => ("geo", "ქართული ენა"),
            "de"                => ("ger", "Deutsch"),
            "de-AT"             => ("ger", "Deutsch(Österreich)"),
            "de-DE"             => ("ger", "Deutsch(Deutschland)"),
            "de-CH"             => ("ger", "Deutsch(Schweiz)"),
            "el"                => ("gre", "Ελληνικά"),
            "kl"                => ("kal", "Kalaallisut"),
            "gn"                => ("grn", "avañe'ẽ"),
            "gu"                => ("guj", "ગુજરાતી"),
            "hak"               => ("hak", "Hak-kâ-fa"),
            "hak-TW"            => ("hak", "Hak-kâ-fa"),
            "ha"                => ("hau", "هَوُسَ"),
            "iw"                => ("heb", "שפה עברית"),
            "hi"                => ("hin", "हिन्दी"),
            "hi-Latn"           => ("hin", "हिंदी(फोनेटिक)"),
            "hu"                => ("hun", "Magyar"),
            "is"                => ("ice", "icelandic"),
            "ig"                => ("ibo", "Asụsụ Igbo"),
            "id"                => ("ind", "Indonesia"),
            "ia"                => ("ina", "Interlingua"),
            "iu"                => ("iku", "ᐃᓄᒃᑎᑐᑦ"),
            "ik"                => ("ipk", "Inupiat"),
            "ga"                => ("gle", "Gaeilge na hÉireann"),
            "it"                => ("ita", "Italiano"),
            "jv"                => ("jav", "ꦧꦱꦗꦮ"),
            "kn"                => ("kan", "ಕನ್ನಡ"),
            "ks"                => ("kas", "कॉशुर"),
            "kk"                => ("kaz", "Қазақ тілі"),
            "km"                => ("khm", "ភាសាខ្មែរ"),
            "rw"                => ("kin", "Ikinyarwanda"),
            "tlh"               => ("tlh", "tlhIngan Hol"),
            "ku"                => ("kur", "Kurdî"),
            "ky"                => ("kir", "кыргыз тили"),
            "lo"                => ("lao", "ພາສາລາວ"),
            "la"                => ("lat", "latīna"),
            "lv"                => ("lav", "latviešu valoda"),
            "ln"                => ("lin", "Lingála"),
            "lt"                => ("lit", "lietuvių kalba"),
            "lb"                => ("ltz", "Lëtzebuergesch"),
            "mk"                => ("mac", "Македонски јазик"),
            "mg"                => ("mlg", "maa.laa.gaas"),
            "ms"                => ("may", "Melayu"),
            "ml"                => ("mal", "മലയാളം"),
            "mt"                => ("mlt", "Lingwa Maltija"),
            "mi"                => ("mao", "Māori"),
            "mr"                => ("mar", "मराठी Marāṭhī"),
            "mas"               => ("mas", "Maasai"),
            "nan"               => ("nan", "閩南語"),
            "nan-TW"            => ("nan", "閩南語(台灣)"),
            "lus"               => ("lus", "Mizo ṭawng"),
            "mo"                => ("mol", "Limba moldovenească"),
            "mn"                => ("mon", "монгол хэл"),
            "my"                => ("bur", "မြန်မာဘာသာ"),
            "na"                => ("nau", "Dorerin Naoero"),
            "nv"                => ("nav", "Diné bizaad"),
            "ne"                => ("nep", "नेपाली Nepālī"),
            "no"                => ("nor", "norsk språk"),
            "fa"                => ("per", "فارسی"),
            "fa-AF"             => ("per", "فارسی"),
            "fa-IR"             => ("per", "فارسی"),
            "pl"                => ("pol", "Polski"),
            "pt"                => ("por", "Português"),
            "pt-BR"             => ("por", "Português(brasil)"),
            "pt-PT"             => ("por", "Português(portugal)"),
            "ro"                => ("rum", "Română"),
            "ru"                => ("rus", "Русский"),
            "ru-Latn"           => ("rus", "Русский(фонетический)"),
            "sr"                => ("srp", "Српски"),
            "sr-Cyrl"           => ("srp", "Српски(ћирилица)"),
            "sr-Latn"           => ("srp", "Српски(латиница)"),
            "sh"                => ("scr", "srpskohrvatski"),
            "sk"                => ("slo", "slovenský"),
            "es"                => ("spa", "Español"),
            "es-419"            => ("spa", "Español(Latinoamérica)"),
            "es-MX"             => ("spa", "Español(México)"),
            "es-ES"             => ("spa", "Español(España)"),
            "es-US"             => ("spa", "Español(Estados Unidos)"),
            "sv"                => ("swe", "Svenska"),
            "tl"                => ("tgl", "Tagalog"),
            "th"                => ("tha", "ไทย"),
            "tr"                => ("tur", "Türkçe"),
            "uk"                => ("ukr", "Українська"),
            "ur"                => ("urd", "Urdu"),
            "vi"                => ("vie", "Tiếng Việt"),
            //太多了，我蚌埠住了，后面懒得查
            //"ie"                => ("", ""),
            //"oc"                => ("",   ""),
            //"or"                => ("",   ""),
            //"om"                => ("",   ""),
            //"ps"                => ("",   ""),
            //"pa"                => ("",   ""),
            //"qu"                => ("",   ""),
            //"rm"                => ("",   ""),
            //"rn"                => ("",   ""),
            //"sm"                => ("",   ""),
            //"sg"                => ("",   ""),
            //"sa"                => ("",   ""),
            //"gd"                => ("",   ""),
            //"sdp"               => ("",   ""),
            //"sn"                => ("",   ""),
            //"scn"               => ("",   ""),
            //"sd"                => ("",   ""),
            //"si"                => ("",   ""),
            //"sl"                => ("",   ""),
            //"so"                => ("",   ""),
            //"st"                => ("",   ""),
            //"su"                => ("",   ""),
            //"sw"                => ("",   ""),
            //"ss"                => ("",   ""),
            //"tg"                => ("",   ""),
            //"ta"                => ("",   ""),
            //"tt"                => ("",   ""),
            //"te"                => ("",   ""),
            //"ti"                => ("",   ""),
            //"to"                => ("",   ""),
            //"ts"                => ("",   ""),
            //"tn"                => ("",   ""),
            //"tk"                => ("",   ""),
            //"tw"                => ("",   ""),
            //"uz"                => ("",   ""),
            //"vo"                => ("",   ""),
            //"cy"                => ("",   ""),
            //"fy"                => ("",   ""),
            //"wo"                => ("",   ""),
            //"xh"                => ("",   ""),
            //"yi"                => ("",   ""),
            //"yo"                => ("",   ""),
            //"zu"                => ("",   ""),
            _ => ("und", "Undetermined")
        };
    }

    #region 字幕接口

    private static async Task<List<Subtitle>?> GetIntlSubtitlesFromApi1Async(string aid, string cid, string epId, int index)
    {
        try
        {
            List<Subtitle> subtitles = new();
            string api = "https://" + (Config.EPHOST == "api.bilibili.com" ? "api.biliintl.com" : Config.EPHOST) + $"/intl/gateway/web/v2/subtitle?episode_id={epId}";
            string json = await GetWebSourceAsync(api);
            using var infoJson = JsonDocument.Parse(json);
            var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitles").EnumerateArray();
            foreach (var sub in subs)
            {
                var lan = sub.GetProperty("lang_key").ToString();
                var url = sub.GetProperty("url").ToString();
                Subtitle subtitle = new()
                {
                    url = url,
                    lan = lan,
                    path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
                };

                //有空的URL 不合法
                if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                    throw new Exception("Bad url");

                subtitles.Add(subtitle);
            }
            return subtitles;
        }
        catch (Exception)
        {
            return null;
        }
    }

    private static async Task<List<Subtitle>?> GetIntlSubtitlesFromApi2Async(string aid, string cid, string epId, int index)
    {
        try
        {
            List<Subtitle> subtitles = new();
            string api = "https://" + (Config.HOST == "api.bilibili.com" ? "api.bilibili.tv" : Config.HOST) +
                         $"/intl/gateway/v2/ogv/view/app/season?ep_id={epId}&platform=android&s_locale=zh_SG" + (Config.TOKEN != "" ? $"&access_key={Config.TOKEN}" : "");
            string json = await GetWebSourceAsync(api);
            using var infoJson = JsonDocument.Parse(json);
            var subs = infoJson.RootElement.GetProperty("result").GetProperty("modules")[0].GetProperty("data")
                .GetProperty("episodes")[index - 1].GetProperty("subtitles").EnumerateArray();
            foreach (var sub in subs)
            {
                var lan = sub.GetProperty("key").ToString();
                var url = sub.GetProperty("url").ToString().Replace("\\\\/", "/");
                Subtitle subtitle = new()
                {
                    url = url,
                    lan = lan,
                    path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
                };

                //有空的URL 不合法
                if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                    throw new Exception("Bad url");

                subtitles.Add(subtitle);
            }
            return subtitles;
        }
        catch (Exception)
        {
            return null;
        }
    }

    private static async Task<List<Subtitle>?> GetSubtitlesFromApi1Async(string aid, string cid, string epId, int index)
    {
        try
        {
            List<Subtitle> subtitles = new();
            string api = $"https://api.bilibili.com/x/web-interface/view?aid={aid}&cid={cid}";
            string json = await GetWebSourceAsync(api);
            using var infoJson = JsonDocument.Parse(json);
            var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitle").GetProperty("list").EnumerateArray();
            foreach (var sub in subs)
            {
                var lan = sub.GetProperty("lan").ToString();
                Subtitle subtitle = new()
                {
                    url = sub.GetProperty("subtitle_url").ToString(),
                    lan = lan,
                    path = $"{aid}/{aid}.{cid}.{lan}.srt"
                };
                subtitles.Add(subtitle);
            }

            //有空的URL 不合法
            if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                throw new Exception("Bad url");

            //无字幕片源 但是字幕没上导致的空列表，尝试从国际接口获取
            //if (subtitles.Count == 0 && !string.IsNullOrEmpty(epId))
            //{
            //    return await GetSubtitlesAsync(aid, cid, epId, true);
            //}
            return subtitles;
        }
        catch (Exception)
        {
            return null;
        }
    }

    private static async Task<List<Subtitle>?> GetSubtitlesFromApi2Async(string aid, string cid, string epId, int index)
    {
        try
        {
            List<Subtitle> subtitles = new();
            string api = $"https://api.bilibili.com/x/player/wbi/v2?cid={cid}&aid={aid}";
            string json = await GetWebSourceAsync(api);
            using var infoJson = JsonDocument.Parse(json);
            var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitle").GetProperty("subtitles").EnumerateArray();
            foreach (var sub in subs)
            {
                var lan = sub.GetProperty("lan").ToString();
                Subtitle subtitle = new()
                {
                    url = sub.GetProperty("subtitle_url").ToString(),
                    lan = lan,
                    path = $"{aid}/{aid}.{cid}.{lan}.srt"
                };
                subtitles.Add(subtitle);
            }

            //有空的URL 不合法
            if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                throw new Exception("Bad url");

            return subtitles;
        }
        catch (Exception)
        {
            return null;
        }
    }

    private static byte[] GetPayload(long aid, long cid)
    {
        var obj = new DmViewReq
        {
            Pid = aid,
            Oid = cid,
            Type = 1,
            Spmid = "main.ugc-video-detail.0.0",
        };
        return AppHelper.PackMessage(obj.ToByteArray());
    }

    private static async Task<List<Subtitle>?> GetSubtitlesFromApi3Async(string aid, string cid, string epId, int index)
    {
        try
        {
            List<Subtitle> subtitles = new();
            //grpc调用接口 protobuf
            string api = "https://app.biliapi.net/bilibili.community.service.dm.v1.DM/DmView";

            var data = GetPayload(Convert.ToInt64(aid), Convert.ToInt64(cid));

            var t = AppHelper.ReadMessage(await GetPostResponseAsync(api, data));
            var resp = new MessageParser<DmViewReply>(() => new DmViewReply()).ParseFrom(t);

            if (resp.Subtitle != null && resp.Subtitle.Subtitles != null)
            {
                subtitles.AddRange(resp.Subtitle.Subtitles.Select(item => new Subtitle() {
                    url = item.SubtitleUrl,
                    lan = item.Lan,
                    path = $"{aid}/{aid}.{cid}.{item.Lan}.srt"
                }));
            }
            //有空的URL 不合法
            if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                throw new Exception("Bad url");

            return subtitles;
        }
        catch (Exception)
        {
            return null;
        }
    }

    #endregion

    public static async Task<List<Subtitle>> GetSubtitlesAsync(string aid, string cid, string epId, int index, bool intl)
    {
        List<Subtitle>? subtitles = new();
        if (intl)
        {
            subtitles = await GetIntlSubtitlesFromApi1Async(aid, cid, epId, index) ?? await GetIntlSubtitlesFromApi2Async(aid, cid, epId, index);
        }
        else
        {
            if (Config.COOKIE == "")
            {
                subtitles = await GetSubtitlesFromApi3Async(aid, cid, epId, index); // 未登录只有APP可以拿到字幕了
            }
            else
            {
                subtitles = await GetSubtitlesFromApi2Async(aid, cid, epId, index)
                            ?? await GetSubtitlesFromApi1Async(aid, cid, epId, index)
                            ?? await GetSubtitlesFromApi3Async(aid, cid, epId, index);
            }

        }

        if (subtitles == null)
        {
            return new List<Subtitle>(); //返回空列表
        }

        //修正 url 协议
        foreach (var item in subtitles)
        {
            if (item.url.StartsWith("//")) item.url = "https:" + item.url;
        }

        return subtitles;
    }

    public static async Task SaveSubtitleAsync(string url, string path)
    {
        if (path.EndsWith(".srt"))
            await File.WriteAllTextAsync(path, ConvertSubFromJson(await GetWebSourceAsync(url)), Encoding.UTF8);
        else
            await File.WriteAllTextAsync(path, await GetWebSourceAsync(url), Encoding.UTF8);
    }

    private static string ConvertSubFromJson(string jsonString)
    {
        StringBuilder lines = new();
        var json = JsonDocument.Parse(jsonString);
        var sub = json.RootElement.GetProperty("body").EnumerateArray().ToList();
        for(int i = 0; i < sub.Count; i++)
        {
            var line = sub[i];
            lines.AppendLine((i + 1).ToString());
            if (line.TryGetProperty("from", out JsonElement from))
            {
                lines.AppendLine($"{FormatTime(from.GetDouble())} --> {FormatTime(line.GetProperty("to").GetDouble())}");
            }
            else
            {
                lines.AppendLine($"{FormatTime(0.0)} --> {FormatTime(line.GetProperty("to").GetDouble())}");
            }
            //有的没有内容
            if (line.TryGetProperty("content", out JsonElement content))
                lines.AppendLine(content.ToString());
            lines.AppendLine();
        }
        return lines.ToString();
    }

    private static string FormatTime(double sec) //64.13
    {
        return TimeSpan.FromSeconds(sec).ToString(@"hh\:mm\:ss\,fff");
    }

    [GeneratedRegex("-[a-z]")]
    private static partial Regex NonCapsRegex();
}